Guild icon
Project Sekai
🔒 CrewCTF 2023 / ✅-rev-ohphp
Avatar
OhPHP - 1000 points
Category: Rev Description: php rev's are cool right, I've heard you liked last year php rev chall, so there another one Author : Fredd Files: No files. Tags: No tags.
Sutx pinned a message to this channel. 07/07/2023 10:02 PM
Avatar
@snwo wants to collaborate 🤝
Avatar
<?php if (in_array(count(get_included_files()), ['1'])) { if (strcmp(php_sapi_name(), 'cli')) { printf('Use php-cli to run the challenge!\n'); } else { printf(gzinflate(base64_decode('1dTBDYAgDAXQe6fgaC8O4DDdfwyhVGmhbaKe/BfQfF8gAQFKz8aRh0JEJY0qIIenINTBEY3qNNVUAfuXzIGitJVqpiBa4yp2U8ZKtKmANzewbaqG2lrAGbNWslOvgD52lULNLfgY9ZiZtdxCsLJ3+Q/2RVuOxji0jyl9aJfrZLJzxhgtS65TWS66wdr7fYzRFtvc/wU9Wpn6BQGc'))); define('F', readline('Flag: ')); if (strcmp(strlen(constant('F')), '41')) { printf('Nope!\n'); } else { if (in_array(substr(constant('F'), 0, 5), ['crew{'])) { if (strstr(strrev(crc32(substr(constant('F'), 5, 4))), '7607349263')) { if (strncmp('A'.'\x1b'.'/'.'k',substr(constant('F'),'5','4')^substr(constant('F'),'9','4'))) { printf('Nope xor!\n'); } else { srand(31337); define('D', openssl_decrypt('wCX3NcMho0BZO0SxG2kHxA==','aes-128-cbc', substr(constant('F'), 0, 16), 2, pack('C4', rand(), rand(), rand(), rand()))); if (in_array(array_sum([ctype_print(constant('D')), strpos(substr(constant('F'), 15, 17), constant('D'))]), ['2'])) { if (strcmp(base64_encode(hash('sha256', substr(constant('F'), 0, 32))^substr(constant('F'), 32)), 'BwdRVwUHBQVF')) { printf('Nope!\n'); } else { printf('Congratulations, this is the right flag!\n'); } } else { printf('Nope!\n'); } } } else { printf('Nope!\n'); } } else { printf('Nope!\n'); } } } } else { printf('Nope!\n'); } (edited)
Avatar
1.starts with crew{
01:07
2. strrev(crc32(flag[5:9]))=='7607349263'
01:07
3. flag[5:9]^flag[9:13]=='A\x1b/k'
Avatar
looks easy
01:08
brute it
01:08
im not on PC so cant try
01:08
i mean flag to char 13 above
01:08
write a php script to brute
Avatar
oh i hate php..
01:09
let me use gpt
Avatar
yeah just use gpt
01:15
also crc32..can use python too
01:16
100**4 is fast
Avatar
flag[5:9] = php_
Avatar
whats the rest?
01:35
0-12 seems handled
Avatar
# crew{php_1s_4}
01:35
oh
Avatar
i'm bruteforcing aes
Avatar
srand(31337); define('D', openssl_decrypt('wCX3NcMho0BZO0SxG2kHxA==','aes-128-cbc', substr(constant('F'), 0, 16), 2, pack('C4', rand(), rand(), rand(), rand()))); if (in_array(array_sum([ctype_print(constant('D')), strpos(substr(constant('F'), 15, 17), constant('D'))]), ['2'])) { if (mstrcmp(base64_encode(hash('sha256', substr(constant('F'), 0, 32))^substr(constant('F'), 32)), 'BwdRVwUHBQVF')) { printf('Nope!\n'); } else { printf('Congratulations, this is the right flag!\n'); } } else { printf('Nope!\n'); }
02:00
next part so weird
Avatar
just brute force
02:03
we know 0-12,
02:04
so we can get till 16 right
02:04
3byte key bruteforce required
02:05
But the check logic is weird
Avatar
it looks like a-z0-9_
02:05
so 37^3
02:05
not much
02:06
wait, strlen is 41 why only 16 char flag?
02:06
Substr index 15 and get 17 chars
Avatar
Avatar
snwo
<?php if (in_array(count(get_included_files()), ['1'])) { if (strcmp(php_sapi_name(), 'cli')) { printf('Use php-cli to run the challenge!\n'); } else { printf(gzinflate(base64_decode('1dTBDYAgDAXQe6fgaC8O4DDdfwyhVGmhbaKe/BfQfF8gAQFKz8aRh0JEJY0qIIenINTBEY3qNNVUAfuXzIGitJVqpiBa4yp2U8ZKtKmANzewbaqG2lrAGbNWslOvgD52lULNLfgY9ZiZtdxCsLJ3+Q/2RVuOxji0jyl9aJfrZLJzxhgtS65TWS66wdr7fYzRFtvc/wU9Wpn6BQGc'))); define('F', readline('Flag: ')); if (strcmp(strlen(constant('F')), '41')) { printf('Nope!\n'); } else { if (in_array(substr(constant('F'), 0, 5), ['crew{'])) { if (strstr(strrev(crc32(substr(constant('F'), 5, 4))), '7607349263')) { if (strncmp('A'.'\x1b'.'/'.'k',substr(constant('F'),'5','4')^substr(constant('F'),'9','4'))) { printf('Nope xor!\n'); } else { srand(31337); define('D', openssl_decrypt('wCX3NcMho0BZO0SxG2kHxA==','aes-128-cbc', substr(constant('F'), 0, 16), 2, pack('C4', rand(), rand(), rand(), rand()))); if (in_array(array_sum([ctype_print(constant('D')), strpos(substr(constant('F'), 15, 17), constant('D'))]), ['2'])) { if (strcmp(base64_encode(hash('sha256', substr(constant('F'), 0, 32))^substr(constant('F'), 32)), 'BwdRVwUHBQVF')) { printf('Nope!\n'); } else { printf('Congratulations, this is the right flag!\n'); } } else { printf('Nope!\n'); } } } else { printf('Nope!\n'); } } else { printf('Nope!\n'); } } } } else { printf('Nope!\n'); } (edited)
is this everything? im reading from phone
02:09
seems last index is 32
02:09
not 41
Avatar
First they check length is 41
02:21
i mean i didnt see an expression where flag 32-41 is checked
Avatar
Yes thats weird
02:23
Or my parsed source incorrect
Avatar
@Violin wants to collaborate 🤝
02:34
@Legoclones wants to collaborate 🤝
Avatar
how we doing on this one
Avatar
I parsed obfuscated php file
02:35
and we know 13 chars
Avatar
gotcha
Avatar
srand(31337); define('D', openssl_decrypt('wCX3NcMho0BZO0SxG2kHxA==','aes-128-cbc', substr(constant('F'), 0, 16), 2, pack('C4', rand(), rand(), rand(), rand()))); if (in_array(array_sum([ctype_print(constant('D')),strpos(substr(constant('F'), 15, 17), constant('D'))]), ['2']))
Avatar
Avatar
snwo
<?php if (in_array(count(get_included_files()), ['1'])) { if (strcmp(php_sapi_name(), 'cli')) { printf('Use php-cli to run the challenge!\n'); } else { printf(gzinflate(base64_decode('1dTBDYAgDAXQe6fgaC8O4DDdfwyhVGmhbaKe/BfQfF8gAQFKz8aRh0JEJY0qIIenINTBEY3qNNVUAfuXzIGitJVqpiBa4yp2U8ZKtKmANzewbaqG2lrAGbNWslOvgD52lULNLfgY9ZiZtdxCsLJ3+Q/2RVuOxji0jyl9aJfrZLJzxhgtS65TWS66wdr7fYzRFtvc/wU9Wpn6BQGc'))); define('F', readline('Flag: ')); if (strcmp(strlen(constant('F')), '41')) { printf('Nope!\n'); } else { if (in_array(substr(constant('F'), 0, 5), ['crew{'])) { if (strstr(strrev(crc32(substr(constant('F'), 5, 4))), '7607349263')) { if (strncmp('A'.'\x1b'.'/'.'k',substr(constant('F'),'5','4')^substr(constant('F'),'9','4'))) { printf('Nope xor!\n'); } else { srand(31337); define('D', openssl_decrypt('wCX3NcMho0BZO0SxG2kHxA==','aes-128-cbc', substr(constant('F'), 0, 16), 2, pack('C4', rand(), rand(), rand(), rand()))); if (in_array(array_sum([ctype_print(constant('D')), strpos(substr(constant('F'), 15, 17), constant('D'))]), ['2'])) { if (strcmp(base64_encode(hash('sha256', substr(constant('F'), 0, 32))^substr(constant('F'), 32)), 'BwdRVwUHBQVF')) { printf('Nope!\n'); } else { printf('Congratulations, this is the right flag!\n'); } } else { printf('Nope!\n'); } } } else { printf('Nope!\n'); } } else { printf('Nope!\n'); } } } } else { printf('Nope!\n'); } (edited)
this is whole code?
Avatar
the code doesnt have 32-41 check somehow
02:39
so im confused
Avatar
4 random numbers -> 1550165595, 1550165595, 2075415819, 700108414
Avatar
encrypted_string = 'wCX3NcMho0BZO0SxG2kHxA==' # Encrypted string encrypted_bytes = base64.b64decode(encrypted_string) key = 'YOUR_KEY_HERE' # 16-byte key iv = bytes([0x5b,0xf,0xb,0x7e]) known = b"crew{php_1s_4" found = False for byte1 in printable_ascii: for byte2 in printable_ascii: for byte3 in printable_ascii: data = (byte1+byte2+byte3).encode() cipher = AES.new(known+data,AES.MODE_CBC,iv+b'\x00'*12) decrypted_data = cipher.decrypt(encrypted_bytes) ## count printable ascii in decrypted data count = 0 for x in decrypted_data: if 32<=x<=126: count+=1 print(count) if found: break if found: break
02:45
i used this
02:45
to bruteforce
02:46
but i don't know the condition
Avatar
it didn't give anything?
02:54
okay I see the problem now
02:55
we can't assume D is going to be printable 100%
02:55
gotta use the next line to figure out how to know the correct value for D
Avatar
@Iyed wants to collaborate 🤝
Avatar
Alright hold up
03:04
if (in_array(array_sum([ctype_print(constant('D')), strpos(substr($F, 15, 17), constant('D'))]), ['2'])) { echo 'here'; }
03:04
is next line
03:04
We know that ctype_print(constant('D')) and strpos(substr($F, 15, 17), constant('D')) have to equal 2
Avatar
printable character in D less then 2
03:05
strpos must return 0
03:05
so printable data in D is 2, doesn't exist in flag[15:15+17]
03:06
is it right?
Avatar
ctype_print() returns true if all characters are printable, false if not
03:06
so 1 or 0
03:07
I misunderstood that returns count of printable
Avatar
yeah just read docs
03:08
now the substring substr($F, 15, 17) will overlap with 3 bytes we brute force, right?
03:08
but in php, it returns flag[15:15+17]
Avatar
so assuming ctype_print() returns 1 because all printable, then that strpos must also return 1, meaning it's actually flag[16:16+16], right?
Avatar
i got it
03:10
bruteforce index 13,14,15
Avatar
yeah, so no overlap then right
Avatar
no overlap from 15 to 31 i think
Avatar
but i didn't see al printable
Avatar
yeah same
03:10
I'm wondering if it's IV
Avatar
then decrypted data is flag[16:16+16] right
Avatar
I'm gonna implement your same brute force script in PHP to see if that will give better results
Avatar
Avatar
snwo
then decrypted data is flag[16:16+16] right
yeah
Avatar
alright this is my PHP script, got no results. @snwo can you check for errors? <?php # turn off openssl_decrypt warnings error_reporting(E_ERROR | E_PARSE); $F_orig = 'crew{php_1s_4'; for ($i = 0; $i <= 255; $i++) { for ($j = 0; $j <= 255; $j++) { for ($k = 0; $k <= 255; $k++) { $byte1 = chr($i); $byte2 = chr($j); $byte3 = chr($k); $F = $F_orig . $byte1 . $byte2 . $byte3; #echo $F . "\n"; # looks good #echo strlen($F) . "\n"; # always 16 # this will reseed each loop srand(31337); $D = openssl_decrypt('wCX3NcMho0BZO0SxG2kHxA==','aes-128-cbc', substr($F, 0, 16), 2, pack('C4', rand(), rand(), rand(), rand())); if (in_array(array_sum([ctype_print($D), strpos(substr($F, 15, 17), $D)]), ['2'])) { echo 'here'; } } } }
Avatar
it looks same as me
03:20
i think that base64 is wrong
03:20
give me a sec
Avatar
how did u initially decode?
Avatar
replace all ('['^'.') to character
03:28
and let chatgpt beautify
03:35
hmm
03:35
it's correct
Avatar
wait
03:45
oh nvm
03:52
okay wait
03:52
maybe I'm blind
03:52
if (strcmp(strlen(constant('F')), '41')) { printf('Nope!\n'); }
03:52
that means the length is NOT 41, right?
03:52
maybe that's why 32-41 never shows up, bc it's only 32 chars long 👀
Avatar
length is 41 maybe
03:53
yes
03:53
it maybe wrong
03:53
then just get 32 bytes
Avatar
almost done decoding and beautifying manually but still haven't found any inconsistencies 😭
04:01
okay wait I found something
04:02
pack is 'L*' instead of 'C4'
04:02
i see
04:02
lmao i forgot
04:05
l4ngu4ge_0f_m4g1c
Avatar
also I think it's '5' instead of '\x1b'
04:05
👀
04:05
crew{php_1s_4_l4ngu4ge_0fm4g1c}?
04:06
only 31 lol
Avatar
crew{php_1s_4_l4ngu4ge_0f_m4g1c_}
04:06
gogo next
Avatar
oh whoops
Avatar
Avatar
snwo
l4ngu4ge_0f_m4g1c
missing _ because not in code format
04:08
crew{php_1s_4_l4ngu4ge_0f_m4g1c_} is 33?
Avatar
Avatar
Legoclones
used /ctf submit
❌ Incorrect flag.
Avatar
hmm
Avatar
crew{php_1s_4_l4ngu4ge_0f_m4g1c_
04:08
32 len
Avatar
okay what are we missing
Avatar
the base64 string is 9 chars
04:13
if (mstrcmp(base64_encode(hash('sha256', substr(constant('F'), 0, 32))^substr(constant('F'), 32)), 'BwdRVwUHBQVF'))
04:14
oh just xor
04:14
wait
Avatar
xor brute -> Key = 32: 55ce7577w Key = 33: 44bd6466v Key = 34: 33ec1311q Key = 35: 22db0200p
04:15
tried those 4 without last char, none were right
04:19
oh wait
04:19
that's cuz it's sha256 hash lol
04:19
😭
04:19
gotta brute 8 bytes lol
Avatar
crew{php_1s_4_l4ngu4ge_0f_m4g1c_5b0e7b6a}
Avatar
Avatar
snwo
used /ctf submit
✅ Well done, challenge solved!
Avatar
niceeeeeeeeeee
Avatar
known = "crew{php_1s_4_l4ngu4ge_0f_m4g1c_" import hashlib import base64 b64 = base64.b64decode(b"BwdRVwUHBQVF") h = hashlib.sha256(known.encode()).hexdigest() print(h) print(b64) print(len(b64)) out = "" for i,j in zip(h,b64): out += chr(ord(i)^j) print(out)
04:24
lol
Exported 154 message(s)